/**
 * Copyright (c) 2016-2020 https://github.com/zhaohuatai
 *
 * contact z_huatai@qq.com
 *  
 */

package org.zfes.snowy.core.util;

import java.io.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.nustaq.serialization.FSTConfiguration;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

public class ZSerialUtil {
	
	//-----------FST Serialize-----------------------------------
	static FSTConfiguration configuration = FSTConfiguration.createDefaultConfiguration();
	public static byte[] fstSerialize(Object obj) {
		if(obj==null) {
	    	return null;
	    }
		return configuration.asByteArray(obj);
	}

	public static Object fstDeserialize(byte[] sec) {
		if(sec==null) {
	    	return null;
	    }
		return configuration.asObject(sec);
	}
	
//-----------Protstuff Serialize-----------------------------------	
	/**
	 * 定义cache ，存储类，
	 */
    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();

    private static Objenesis objenesis = new ObjenesisStd(true);

    @SuppressWarnings("unchecked")
    private static <T> Schema<T> getSchema(Class<T> cls) {
        Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(cls);
            if (schema != null) {
                cachedSchema.put(cls, schema);
            }
        }
        return schema;
    }
    /**
     * 序列化（对象 -> 字节数组）
     */
    @SuppressWarnings("unchecked")
    public static <T> byte[] protstuffSerialize(T obj) {
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(cls);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    /**
     * 反序列化（字节数组 -> 对象）
     */
    public static <T> T protstuffDeserialize(byte[] data, Class<T> cls) {
        try {
            T message = (T) objenesis.newInstance(cls);
            Schema<T> schema = getSchema(cls);
            ProtostuffIOUtil.mergeFrom(data, message, schema);
            return message;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
//-----------kryo Serialize-----------------------------------	
    
	 public final static Kryo kryo = new Kryo();
	 static {
	        kryo.setMaxDepth(4);
	    }
	/**
	 * 序列化
	 * @param value
	 * @return
	 */
   public static byte[] kryoSerialize(Object value) {
		byte[] buffer = new byte[2048];
		Output output=null;
		try{
			 output = new Output(buffer);
			kryo.writeClassAndObject(output, value);
			buffer=output.toBytes();
			return buffer;
		} catch (Exception e) {
		}finally{
			if(output!=null){
				output.close();
			}
			
		}
		return buffer;
   }

   /**
    * 反序列化
    * @param in
    * @return
    */
	public static Object kryoDeserialize(byte[] in) {
		Input input =null;
		try {
			 input = new Input(in);
			Object obj=kryo.readClassAndObject(input);
			input.close();
			return obj;
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if(input!=null){
				input.close();
			}
		}
		return kryo;
   }
	
//-----------Hessian Serialize-----------------------------------
	
	public static  byte[] hessianSerialize(Object obj){  
	    if(obj==null) {
	    	return null;
	    }
	    ByteArrayOutputStream os = new ByteArrayOutputStream();  
	    HessianOutput ho = new HessianOutput(os);  
	    try {
			ho.writeObject(obj);
		} catch (IOException e) {
			e.printStackTrace();
		}  
	    return os.toByteArray();  
	}  
	  
	public static Object hessianDeserialize(byte[] by){  
	    if(by==null) {
	    	return null;
	    }
	    ByteArrayInputStream is = new ByteArrayInputStream(by);  
	    HessianInput hi = new HessianInput(is);  
	    try {
			return hi.readObject();
		} catch (IOException e) {
			e.printStackTrace();
		} 
	    return null;
	} 
	
//-----------JDK Serialize-----------------------------------
	
	/**
	 * 序列化
	 * @param value
	 * @return
	 */
    public static byte[] jdkSerialize(Object value) {
        if (value == null) {
            return null;
        }
        byte[] rv = null;
        ByteArrayOutputStream bos = null;
        ObjectOutputStream os = null;
        try {
            bos = new ByteArrayOutputStream();
            os = new ObjectOutputStream(bos);
            os.writeObject(value);
            os.close();
            bos.close();
            rv = bos.toByteArray();
        } catch (Exception e) {
        	e.printStackTrace();
        } finally {
            close(os);
            close(bos);
        }
        return rv;
    }

    /**
     * 反序列化
     * @param in
     * @return
     */
    public static Object jdkDeserialize(byte[] in) {
        return jdkDeserialize(in, Object.class);
    }

    /**
     * 反序列化
     * @param in
     * @param requiredType
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T jdkDeserialize(byte[] in, Class<T> requiredType) {
        Object rv = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream is = null;
        try {
            if (in != null) {
                bis = new ByteArrayInputStream(in);
                is = new ObjectInputStream(bis);
                rv = is.readObject();
            }
        } catch (Exception e) {
        } finally {
            close(is);
            close(bis);
        }
        return (T) rv;
    }

    private static void close(Closeable closeable) {
        if (closeable != null)
            try {
                closeable.close();
            } catch (IOException e) {
            	e.getMessage();
            }
    }
}
